package com.scheduling.product.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.scheduling.common.exception.BusinessException;
import com.scheduling.material.mapper.MaterialMapper;
import com.scheduling.product.dto.OperationDTO;
import com.scheduling.product.dto.OperationMaterialDTO;
import com.scheduling.product.entity.Operation;
import com.scheduling.product.entity.OperationMaterial;
import com.scheduling.product.mapper.OperationMapper;
import com.scheduling.product.mapper.OperationMaterialMapper;
import com.scheduling.product.mapper.ProcessRouteMapper;
import com.scheduling.product.service.OperationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 工序业务层实现类
 *
 * @author 开发团队
 * @since 2025-05-29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OperationServiceImpl extends ServiceImpl<OperationMapper, Operation> implements OperationService {

    private final OperationMapper operationMapper;
    private final OperationMaterialMapper operationMaterialMapper;
    private final ProcessRouteMapper processRouteMapper;
    private final MaterialMapper materialMapper;

    @Override
    public List<OperationDTO> getOperationsByRouteId(String routeId) {
        log.info("根据工艺路线ID查询工序列表：{}", routeId);

        if (StrUtil.isBlank(routeId)) {
            throw new BusinessException("工艺路线ID不能为空");
        }

        List<OperationDTO> operations = operationMapper.selectOperationsByRouteId(routeId);

        // 为每个工序查询原料列表
        for (OperationDTO operation : operations) {
            List<OperationMaterialDTO> materials =
                    operationMaterialMapper.selectMaterialsByOperationId(operation.getOperationId());
            operation.setMaterials(materials);
        }

        return operations;
    }

    @Override
    public OperationDTO getOperationDetailById(String operationId) {
        log.info("根据ID查询工序详情：{}", operationId);

        if (StrUtil.isBlank(operationId)) {
            throw new BusinessException("工序ID不能为空");
        }

        Operation operation = operationMapper.selectById(operationId);
        if (operation == null) {
            throw new BusinessException("工序不存在");
        }

        OperationDTO operationDTO = new OperationDTO();
        BeanUtil.copyProperties(operation, operationDTO);

        // 查询原料列表
        List<OperationMaterialDTO> materials = operationMaterialMapper.selectMaterialsByOperationId(operationId);
        operationDTO.setMaterials(materials);

        return operationDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addOperation(OperationDTO operationDTO) {
        log.info("添加工序：{}", operationDTO);

        // 验证工艺路线是否存在
        if (processRouteMapper.selectById(operationDTO.getRouteId()) == null) {
            throw new BusinessException("工艺路线不存在");
        }

        // 检查工序编号唯一性
        if (checkOperationCodeExists(operationDTO.getRouteId(), operationDTO.getOperationCode(), null)) {
            throw new BusinessException("工序编号在工艺路线内已存在");
        }

        // 检查工序序号唯一性
        if (checkSequenceNoExists(operationDTO.getRouteId(), operationDTO.getSequenceNo(), null)) {
            throw new BusinessException("工序序号在工艺路线内已存在");
        }

        Operation operation = new Operation();
        BeanUtil.copyProperties(operationDTO, operation);

        boolean result = save(operation);
        if (!result) {
            throw new BusinessException("添加工序失败");
        }

        // 保存工序原料关联
        saveOperationMaterials(operation.getOperationId(), operationDTO.getMaterials());

        log.info("添加工序成功，工序ID：{}", operation.getOperationId());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOperation(OperationDTO operationDTO) {
        log.info("更新工序：{}", operationDTO);

        if (StrUtil.isBlank(operationDTO.getOperationId())) {
            throw new BusinessException("工序ID不能为空");
        }

        // 检查工序是否存在
        Operation existOperation = operationMapper.selectById(operationDTO.getOperationId());
        if (existOperation == null) {
            throw new BusinessException("工序不存在");
        }

        // 验证工艺路线是否存在
        if (processRouteMapper.selectById(operationDTO.getRouteId()) == null) {
            throw new BusinessException("工艺路线不存在");
        }

        // 检查工序编号唯一性（排除当前工序）
        if (checkOperationCodeExists(operationDTO.getRouteId(), operationDTO.getOperationCode(),
                operationDTO.getOperationId())) {
            throw new BusinessException("工序编号在工艺路线内已存在");
        }

        // 检查工序序号唯一性（排除当前工序）
        if (checkSequenceNoExists(operationDTO.getRouteId(), operationDTO.getSequenceNo(),
                operationDTO.getOperationId())) {
            throw new BusinessException("工序序号在工艺路线内已存在");
        }

        Operation operation = new Operation();
        BeanUtil.copyProperties(operationDTO, operation);

        boolean result = updateById(operation);
        if (!result) {
            throw new BusinessException("更新工序失败");
        }

        // 更新工序原料关联
        // 先删除旧的关联
        operationMaterialMapper.deleteByOperationId(operationDTO.getOperationId());
        // 保存新的关联
        saveOperationMaterials(operationDTO.getOperationId(), operationDTO.getMaterials());

        log.info("更新工序成功，工序ID：{}", operation.getOperationId());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteOperation(String operationId) {
        log.info("删除工序：{}", operationId);

        if (StrUtil.isBlank(operationId)) {
            throw new BusinessException("工序ID不能为空");
        }

        // 检查工序是否存在
        Operation operation = operationMapper.selectById(operationId);
        if (operation == null) {
            throw new BusinessException("工序不存在");
        }

        // 检查工序是否被生产任务引用
        int productionTaskCount = operationMapper.countProductionTasksByOperationId(operationId);
        if (productionTaskCount > 0) {
            throw new BusinessException("删除失败：该工序正在被 " + productionTaskCount + " 个生产任务引用，请先删除相关生产任务或排产计划");
        }

        // 删除工序原料关联
        operationMaterialMapper.deleteByOperationId(operationId);

        boolean result = removeById(operationId);
        if (result) {
            log.info("删除工序成功，工序ID：{}", operationId);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteOperations(List<String> operationIds) {
        log.info("批量删除工序，数量：{}", operationIds.size());

        if (operationIds == null || operationIds.isEmpty()) {
            throw new BusinessException("工序ID列表不能为空");
        }

        for (String operationId : operationIds) {
            deleteOperation(operationId);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteOperationsByRouteId(String routeId) {
        log.info("根据工艺路线ID删除所有工序：{}", routeId);

        if (StrUtil.isBlank(routeId)) {
            throw new BusinessException("工艺路线ID不能为空");
        }

        // 查询工艺路线下的所有工序
        LambdaQueryWrapper<Operation> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Operation::getRouteId, routeId);
        List<Operation> operations = operationMapper.selectList(wrapper);

        // 删除每个工序及其原料关联
        for (Operation operation : operations) {
            deleteOperation(operation.getOperationId());
        }

        log.info("删除工艺路线下的工序完成，工艺路线ID：{}，工序数量：{}", routeId, operations.size());
        return true;
    }

    @Override
    public boolean checkOperationCodeExists(String routeId, String operationCode, String excludeId) {
        if (StrUtil.isBlank(routeId) || StrUtil.isBlank(operationCode)) {
            return false;
        }
        Integer count = operationMapper.checkOperationCodeExists(routeId, operationCode, excludeId);
        return count != null && count > 0;
    }

    @Override
    public boolean checkSequenceNoExists(String routeId, Integer sequenceNo, String excludeId) {
        if (StrUtil.isBlank(routeId) || sequenceNo == null) {
            return false;
        }
        Integer count = operationMapper.checkSequenceNoExists(routeId, sequenceNo, excludeId);
        return count != null && count > 0;
    }

    @Override
    public List<Operation> getOperationsByMachineTypeId(String machineTypeId) {
        log.info("根据机器类型ID查询工序列表：{}", machineTypeId);

        if (StrUtil.isBlank(machineTypeId)) {
            throw new BusinessException("机器类型ID不能为空");
        }

        LambdaQueryWrapper<Operation> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Operation::getMachineTypeId, machineTypeId)
                .orderByAsc(Operation::getSequenceNo);

        return list(wrapper);
    }

    /**
     * 保存工序原料关联
     */
    private void saveOperationMaterials(String operationId, List<OperationMaterialDTO> materials) {
        if (materials == null || materials.isEmpty()) {
            return;
        }

        for (OperationMaterialDTO materialDTO : materials) {
            // 验证原料是否存在
            if (StrUtil.isNotBlank(materialDTO.getMaterialId()) &&
                    materialMapper.selectById(materialDTO.getMaterialId()) == null) {
                throw new BusinessException("原料不存在：" + materialDTO.getMaterialId());
            }

            OperationMaterial operationMaterial = new OperationMaterial();
            BeanUtil.copyProperties(materialDTO, operationMaterial);
            operationMaterial.setOperationId(operationId);
            operationMaterialMapper.insert(operationMaterial);
        }
    }
} 